package org.mtmi.ui.controls;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Point;
import org.mtmi.tuio.listeners.DragListener;
import org.mtmi.tuio.listeners.SingleTapListener;
import org.mtmi.tuio.listeners.TUIODragEvent;
import org.mtmi.tuio.listeners.TUIOTapEvent;
import org.mtmi.ui.GraphicUtil;
import org.mtmi.ui.M;
import org.mtmi.ui.actions.Action;
import org.mtmi.ui.actions.IAction;
import org.mtmi.ui.actions.IActionListener;
import org.mtmi.ui.actions.SeparatorAction;
import org.mtmi.ui.controls.listeners.ControlTappedListener;
import org.mtmi.ui.controls.listeners.TapEvent;
import org.mtmi.ui.layouts.LayoutData;
import org.mtmi.ui.scenes.COLOR;
import org.mtmi.ui.scenes.Scene;

public class ToolBar extends Control
  implements ITouchableControl, SingleTapListener, DragListener, IActionListener
{

	private List<IAction> actions;
	private List<Control> controls;
	private int bg_alpha;
	private int spacing; //see computeRawSize(); this is vertical or horizontal spacing
	private boolean visible;
	
	public ToolBar( Scene scene, int flags )
	{
		super( scene, flags );
		visible = true;
		setForeground( COLOR.grey100 );
		setBackground( COLOR.grey30  );
		actions = new ArrayList<IAction>();
		controls = new ArrayList<Control>();
		bg_alpha = 255;
	}

	public boolean isVisible() { return visible; }
	
	public void setVisible( boolean v ) {
		if( v!=visible ) {
			visible = v;
			if( visible ) {
				for( Control c : controls ) {
					getScene().addControl( c );
				}
			} else {
				for( Control c : controls ) {
					getScene().removeControl( c );
				}
			}
			redraw();
		}
	}
	
	/** @return true if toolbar is layouted verticaly, false for horizontally */
	public boolean isVertical() {
		return (getFlags() & M.VERTICAL)!=0;
	}
	
	/** @returnaa copy of the actions currently added to toolbar */
	public List<IAction> getActions() {
		ArrayList<IAction> cpy = new ArrayList<IAction>();
		cpy.addAll( actions );
		return cpy;
	}
	
	/**
	 * Add action to toolbar, when finished to add actions, you must set layout=true.
	 * A control is automatically created for action.
	 */
	public void addAction( IAction a, boolean layout ) {
		actions.add( a );
		if( a instanceof Action ) ((Action)a).addActionListener( this );
		controls.add( createControl( a ) );
		if( layout ) {
			updateLayout();
		}
	}
	
	private void updateLayout() {
		LayoutData ld = new LayoutData(getScene() );
		size = computeSize( ld );
		layoutControls( ld );
	}
	
	/** 
	 * Add a control to layout in toolbar, this method must be called for control
	 * that haven't action associated with.
	 * @param c      the control to add.
	 * @param layout true to layout the toolbar, false if there are next call (avoid layout()
	 */
	public void addControl( Control c, boolean layout ) {
		actions.add( null ); //add action to keep same list size
		controls.add( c );
		if( layout ) {
			updateLayout();
		}
	}
	
	public void setBackgroundAlpha( int a ) { bg_alpha=a; }
	
//	private static final String D_ACTION = "action@ToolBar"; //$NON-NLS-1$
	
	private Control createControl( final IAction a ) {
		//preserve the editor flag of toolbar:
		int f = getFlags()&M.SCENE_EDITOR_CONTROL;
		
		if( a instanceof SeparatorAction ) {
			int fv = isVertical() ? M.HORIZONTAL : M.VERTICAL;
			return new ToolBarSeparator( getScene(), f|fv );
		}
		else {
			//Button btn = new Button( getScene(), getFlags()&M.SCENE_EDITOR_CONTROL );
			ToolBarButton btn = new ToolBarButton( getScene(), a, f );
			/*TODO:for classic btn:	IDrawable d = a.getImage();
		if( d != null ) {
			btn.setImage( d );
		} else {
			btn.setText( a.getText() );
		}*/
			//		btn.setData( D_ACTION, a );
			btn.addTapListener( new ControlTappedListener() {
				@Override
				public void controlTappedUp(TapEvent event) {
					a.run();
				}

				@Override
				public void controlTappedDown(TapEvent event) {}
			});
			return btn;
		}
	}

	@Override
	public Point computeSize( LayoutData l ) {
		Point p = computeRawSize( l );
		p.x += 20;
		p.y += 20;
		return p;
	}
	
	/** return size without margin around actions */
	private Point computeRawSize( LayoutData l ) {
		if( isVertical() ) {
			int wx = 0; 
			int h = 0;
			for( Control c : controls ) {
				if( c==null ) continue;
				Point sz = c.computeSize( l );
				wx = Math.max( wx, sz.x );
				h += sz.y;
			}
			spacing = Math.max( 8, wx/3 );
			if( actions.size()>0 ) {
				h += (actions.size()-1)*spacing;
			}
			return new Point( wx, h );
		} else { //horizontal
			int w = 0; 
			int hx = 0;
			for( Control c : controls ) {
				if( c==null ) continue;
				Point sz = c.computeSize( l );
				w += sz.x;
				hx = Math.max( hx, sz.y );
			}
			spacing = Math.max( 8, hx/3 );
			if( actions.size()>0 ) {
				w += (actions.size()-1)*spacing;
			}
			return new Point( w, hx );
		}
	}

	@Override
	public boolean containsTouch(TouchControlEvent e) {
		if( visible ) {
			return GraphicUtil.rectContains( location, size, e.x, e.y );
		} 
		return false;
	}

	@Override
	public void paint( GC gc ) {
		if( !visible ) return ;
		int r ;
		if( isVertical() ) {
			r = size.x/4;
		} else {
			r = size.y/4;
		}
		gc.setLineWidth( borderWidth );
		int b2=borderWidth/2;
		Path p = GraphicUtil.getRoundRectangle( gc, location.x+b2, location.y+b2, size.x-borderWidth, size.y-borderWidth, r,r );
		gc.setForeground( getForeground() );
		gc.setBackground( getBackground() );
		gc.setAlpha( bg_alpha );
		gc.fillPath( p );
		gc.setAlpha( 255 );
		gc.drawPath( p );
		p.dispose();
		
	}

	private int dgdx, dgdy;
	private boolean moving;
	
	@Override
	public void tapDown( TUIOTapEvent event ) {
		dgdx = location.x-event.ex;
		dgdy = location.y-event.ey;
		moving = true;
	}

	@Override
	public void tapUp( TUIOTapEvent event ) {
		moving = false;
	}

	@Override
	public void drag( TUIODragEvent event ) {
		if( moving ) {
			int lx = dgdx+event.ex;
			int ly = dgdy+event.ey;
			Point sz = getScene().getSize();
			if( lx < 0 ) lx = 0;
			else if( lx+size.x > sz.x ) lx = sz.x-size.x;
			if( ly < 0 ) ly = 0;
			else if( ly+size.y > sz.y ) ly = sz.y-size.y;
			if( lx != location.x || ly != location.y ) {
				setLocation( lx, ly );
				scene.redraw();
			}
		}
	}
	
	private void layoutControls( LayoutData ld )
	{
		if( ld==null ) ld = new LayoutData( getScene() );
		Point rs = computeRawSize( ld );
		
		if( isVertical() ) {
			int w = rs.x;
			int x = location.x+10;
			int y = location.y+10;

			ld.lw = w;
			ld.lh = w;

			for( Control c : controls ) {
				if( c==null ) continue;
				Point sz = c.computeSize( ld );
				c.setBounds( x, y, Math.max( w, sz.x ), sz.y );
				Point s = c.getSize();
				y += s.y+spacing;
			}
		} else {
			int h = rs.y;
			int x = location.x+10;
			int y = location.y+10;
			
			ld.lw = h;
			ld.lh = h;
			
			for( Control c : controls ) {
				if( c==null ) continue;
				Point sz = c.computeSize( ld );
				c.setBounds( x, y, sz.x, Math.max( h, sz.y ) );
				Point s = c.getSize();
				x += s.x+spacing;
			}
		}
		redraw();
	}
	
	@Override
	public void setScene(Scene scene) {
		super.setScene(scene);
		for( Control c: controls ) {
			if( c != null ) {
				c.setScene( scene );
			}
		}
	}
	
	/** @return the control corresponding to given action, null for none */
	protected Control getControl( IAction a ) {
		int idx = actions.indexOf( a );
		if( idx < 0 ) return null;
		return controls.get( idx );
	}
	
	@Override
	public void setBounds( int x, int y, int w, int h ) {
		int dx = location.x-x;
		int dy = location.y-y;
		super.setBounds( x, y, w, h );
		//move toolbar controls too:
		for( Control c : controls ) {
			if( c!=null ) {
				Point l = c.getLocation();
				c.setLocation( l.x-dx, l.y-dy );
			}
		}
	}
	
	@Override
	public void dispose() {
		super.dispose();
		for( Control c : controls ) {
			if( c!=null ) {
				c.dispose();
			}
		}
		for( IAction a : actions ) {
			if( a instanceof Action ) {
				((Action)a).removeActionListener( this );
			}
		}
	}

	@Override
	public void actionChanged(IAction action) {
		updateLayout();
		redraw();
	}
}
